using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

public class BoardManager : MonoBehaviour {
	
	public BoardPiece[] Pieces;	// 1d array to represent a 2d array
	public GameObject ChallengerPrefab;	// Prefab
	public GameObject OppositionPrefab; // Prefab
	public GameObject ColliderCover;
	[HideInInspector]
	public bool IsChallengersTurn = true;
	[HideInInspector]
	public int ActionsRemaining = 2; // when this number hits zero, the next player takes a turn

	private const int NUMROWS = 9;
	private const int NUMCOLS = 9;

	private TokenPiece _selectedToken;

	private bool _hasUsedSelectedToken = false; // to keep track when we have or haven't used an action

	[HideInInspector]
	public bool _isChallengerGeneralDead = false;
	[HideInInspector]
	public bool _isOppositionGeneralDead = false;
	[HideInInspector]
	public bool GameOver = false;

	public PlayerBehavior Opposition;
	public PlayerBehavior Challenger;

	// Use this for initializations
	void Start () 
	{
		// Events will be received in the BoardManager class
		// this prevents the classes from trying to operate when doing so is invalid
		foreach (TokenPiece token in Challenger.Tokens) 
		{
			token.OnTokenSelected += OnChallengerTokenSelected;
			token.OnTokenAttacked += OnChallengerTokenAttacked;
			if(token.TokenID == ID.ID_SOLDIER || token.TokenID == ID.ID_CANNON)
				token.OnAbilityTriggered += OnChallengerAbilityTriggered;
		}
		foreach (TokenPiece token in Opposition.Tokens) 
		{
			token.OnTokenSelected += OnOppositionTokenSelected;
			token.OnTokenAttacked += OnOppositionTokenAttacked;
			if(token.TokenID == ID.ID_SOLDIER || token.TokenID == ID.ID_CANNON)
				token.OnAbilityTriggered += OnOppositionAbilityTriggered;
		}
		foreach (BoardPiece piece in Pieces)
			piece.OnPieceSelected += OnBoardPieceSelected;

		ResetGame ();

		// TODO: BASE THIS ON PLAYER'S STARTING STAND AS CHOSEN BY PLAYER
		// hard coded for now
		Challenger.Tokens [0].BoardPosition = 38;
		Challenger.Tokens [1].BoardPosition = 29;
		Challenger.Tokens [2].BoardPosition = 47;
		Challenger.Tokens [3].BoardPosition = 28;
		Challenger.Tokens [4].BoardPosition = 46;
		Challenger.Tokens [5].BoardPosition = 37;
		Challenger.Tokens [6].BoardPosition = 36;
		Opposition.Tokens [0].BoardPosition = 42;
		Opposition.Tokens [1].BoardPosition = 26;
		Opposition.Tokens [2].BoardPosition = 62;
		Opposition.Tokens [3].BoardPosition = 34;
		Opposition.Tokens [4].BoardPosition = 52;
		Opposition.Tokens [5].BoardPosition = 43;
		Opposition.Tokens [6].BoardPosition = 44;


	}

	/// <summary>
	/// Raises the challenger ability triggered event.
	/// </summary>
	/// <param name="token">Token.</param>
	void OnChallengerAbilityTriggered(GameObject token)
	{
		if (!IsChallengersTurn)
			return;
		if (Challenger.HandleFriendlyTokenSpecialAbility (token.GetComponent<TokenPiece> ())) 
		{
			ActionsRemaining--;
			if(ActionsRemaining == 0)
				EndTurn();
		}
	}
	
	/// <summary>
	/// Raises the challenger token selected event.
	/// </summary>
	/// <param name="token">Token GameObject passed in the delegate.</param>
	void OnChallengerTokenSelected(GameObject token)
	{
		if (!IsChallengersTurn) 
		{
			if(_selectedToken != null && Pieces[token.GetComponent<TokenPiece>().BoardPosition].Selectable)
			{
				TokenPiece tokenScript = token.GetComponent<TokenPiece>();
				if(Opposition.HandleEnemyTokenAttacked(_selectedToken,ref tokenScript))
				{
					// destroy token!
					Challenger.Tokens.Remove(Challenger.Tokens.Find(p=>p.gameObject == token));
					OnBoardPieceSelected(Pieces[tokenScript.BoardPosition].gameObject);
					Destroy(token);
				}
			}
		} 
		else 
		{
			if(_hasUsedSelectedToken)
				return;	
			DetermineSelectableTiles(token);
		}
	}

	/// <summary>
	/// Raises the challenger token attacked event.
	/// </summary>
	/// <param name="token">Token.</param>
	void OnChallengerTokenAttacked(GameObject token)
	{
		if (!IsChallengersTurn && _selectedToken != null) 
		{
			if(Pieces[token.GetComponent<TokenPiece>().BoardPosition].Selectable)
			{
				TokenPiece tokenScript = token.GetComponent<TokenPiece>();
				Opposition.HandleEnemyTokenAttacked(_selectedToken,ref tokenScript);

				// destroy token!
				Challenger.Tokens.Remove(Challenger.Tokens.Find(p=>p.gameObject == token));

				if(tokenScript.TokenID == ID.ID_GENERAL)
				{
					// If the opposition hit the challenger, it's game over!
					_isChallengerGeneralDead = true;
				}

				if(_selectedToken._attack == 0)
					EndAction();
				else
					DetermineSelectableTiles(_selectedToken.gameObject);
				Destroy (token);
			}
		}
	}

	void OnOppositionAbilityTriggered(GameObject token)
	{
		if (IsChallengersTurn)
			return;

		TokenPiece tokenScript = token.GetComponent<TokenPiece> ();
		if (Opposition.HandleFriendlyTokenSpecialAbility (tokenScript)) 
		{
			if (tokenScript.TokenID == ID.ID_SOLDIER) 
			{
				tokenScript.SwitchStance();
				ActionsRemaining--;
				if(ActionsRemaining == 0)
					EndTurn();
			} 
		}
	}
	
	/// <summary>
	/// Raises the opposition token selected event.
	/// </summary>
	/// <param name="token">Token GameObject passed in the delegate.</param>
	void OnOppositionTokenSelected(GameObject token)
	{
		if (IsChallengersTurn) 
		{
			//if challenger selects opposition token, destroy that token
			if(_selectedToken != null && Pieces[token.GetComponent<TokenPiece>().BoardPosition].Selectable)
			{
				TokenPiece tokenScript = token.GetComponent<TokenPiece>();
				if(Challenger.HandleEnemyTokenAttacked(_selectedToken,ref tokenScript))
				{
					// destroy token!
					Challenger.Tokens.Remove(Challenger.Tokens.Find(p=>p.gameObject == token));
					OnBoardPieceSelected(Pieces[tokenScript.BoardPosition].gameObject);
					Destroy(token);
				}
			}
		} 
		else 
		{
			if (_hasUsedSelectedToken) 
				return;
			DetermineSelectableTiles (token);
		}
	}

	void OnOppositionTokenAttacked(GameObject token)
	{
		if (IsChallengersTurn && _selectedToken != null) 
		{
			TokenPiece tokenScript = token.GetComponent<TokenPiece>();
			if(Pieces[tokenScript.BoardPosition].Selectable)
			{
				Challenger.HandleEnemyTokenAttacked(_selectedToken,ref tokenScript);
				
				// destroy token!
				Opposition.Tokens.Remove(Challenger.Tokens.Find(p=>p.gameObject == token));
				
				if(tokenScript.TokenID == ID.ID_GENERAL)
				{
					// If the opposition hit the challenger, it's game over!
					_isOppositionGeneralDead = true;
				}
				
				if(_selectedToken._attack == 0)
					EndAction();
				else
					DetermineSelectableTiles(_selectedToken.gameObject);
				Destroy (token);
			}
		}
	}
	
	/// <summary>
	/// Raises the board piece selected event.
	/// </summary>
	/// <param name="piece">Piece GameObject passed in the delegate..</param>
	void OnBoardPieceSelected(GameObject piece)
	{
		if (_selectedToken == null)
			return;
		if (_selectedToken._movement == 0)
			return;
		// remove the board highlights
		GameHelper.RemoveHighlights (_selectedToken);
		// if the player has attacked, the player cannot move this action
		int newBoardPosition = Array.FindIndex (Pieces, p => p.gameObject == piece);
		int newRow = newBoardPosition / NUMCOLS;
		int newCol = newBoardPosition % NUMCOLS;
		int row = _selectedToken.BoardPosition / NUMCOLS;
		int col = _selectedToken.BoardPosition % NUMCOLS;
		int distance = GameHelper.CalculateAbsoluteDistance (newCol, newRow, col, row);
		_selectedToken._attack -= distance;
		_selectedToken._movement -= distance;
		_selectedToken.BoardPosition = newBoardPosition;
		Vector3 positionedPiece = Pieces [_selectedToken.BoardPosition].transform.position;
		_selectedToken.gameObject.transform.position = new Vector3(positionedPiece.x,_selectedToken.transform.position.y,positionedPiece.z);

		if (Math.Max(_selectedToken._attack,_selectedToken._movement) <= 0) 
		{
			// remove the next action
			// if there are no more actions remaining, it is the next player's turn
			ActionsRemaining--;
			_hasUsedSelectedToken = false;
			if (ActionsRemaining == 0) 
				EndTurn();
		} 
		else 
		{
			_hasUsedSelectedToken = true;
			DetermineSelectableTiles (_selectedToken.gameObject);
		}
	}

	/// <summary>
	/// Determines the selectable tiles.
	/// </summary>
	/// <param name="selectedToken">Selected token.</param>
	void DetermineSelectableTiles(GameObject selectedToken)
	{
		// with the number of possible moves/attacks, find the valid possible moves
		TokenPiece token = selectedToken.GetComponent<TokenPiece> ();
		if(token == null)
			throw new MissingComponentException();
		GameHelper.RemoveHighlights(_selectedToken);

		if(token.TokenID == ID.ID_SOLDIER && token.IsSoldierInDefenseMode == true)
			return;

		_selectedToken = token;
		int currentPosition = token.BoardPosition;
		int row = currentPosition/NUMCOLS;
		int col = currentPosition%NUMCOLS;
		int newRow, newCol;
		int maxMoves = Math.Max (token._attack, token._movement);

		for (int i = -maxMoves; i <= maxMoves; ++i) 
		{
			newCol = col+i;
			if(newCol < 0 || newCol >= NUMCOLS)
				continue;
			for(int j = -maxMoves; j <= maxMoves; ++j)
			{
				newRow = row+j;
				if(newRow >= 0 && newRow < NUMROWS)
				{
					if(Array.Exists((IsChallengersTurn ? Challenger.Tokens.ToArray() : Opposition.Tokens.ToArray()),p=>p.BoardPosition == newRow*NUMCOLS+newCol))
						continue;

					Pieces[newRow*NUMCOLS+newCol].ToggleSelectable(true);
					_selectedToken._possibleMoves.Add(Pieces[newRow*NUMCOLS+newCol]);
				
				}
			}
		}
	}

	// HELPERS
	void ResetActions(bool isChallengersTurn)
	{
		(isChallengersTurn ? Challenger : Opposition).ResetTokens ();
	}

	/// <summary>
	/// Ends the turn.
	/// </summary>
	public void EndTurn()
	{
		GameHelper.RemoveHighlights (_selectedToken);
		IsChallengersTurn = !IsChallengersTurn;
		ActionsRemaining = 2;
		_selectedToken = null;
		_hasUsedSelectedToken = false;
		ResetActions (IsChallengersTurn);

		// if a general is dead and it's the end of the opposition's turn...
		if (IsChallengersTurn && (_isChallengerGeneralDead || _isOppositionGeneralDead))
			GameOverScreen ();
	}

	/// <summary>
	/// Ends the action.
	/// </summary>
	public void EndAction()
	{
		_selectedToken._attack = 0;
		_hasUsedSelectedToken = false;
		ActionsRemaining--;
		if (ActionsRemaining == 0)
			EndTurn ();
		GameHelper.RemoveHighlights (_selectedToken);
	}

	/// <summary>
	/// Resets the game.
	/// </summary>
	public void ResetGame()
	{
		// reset the game
		GameOver = false;
		IsChallengersTurn = true;
		_isChallengerGeneralDead = false;
		_isOppositionGeneralDead = false;
		GameHelper.RemoveHighlights (_selectedToken);
		ActionsRemaining = 2;

		Challenger.RemoveTokens ();
		Opposition.RemoveTokens ();

		// add prefab tokens
		GameObject challengerCopy = Instantiate (ChallengerPrefab) as GameObject;
		GameObject oppositionCopy = Instantiate (OppositionPrefab) as GameObject;
		for (int index = 0; index < challengerCopy.transform.childCount; ++index) 
		{
			GameObject cToken = challengerCopy.transform.GetChild(index).gameObject;
			Challenger.Tokens.Add(cToken.GetComponent<TokenPiece>());
			cToken.GetComponent<TokenPiece>().OnTokenAttacked += OnChallengerTokenAttacked;
			cToken.GetComponent<TokenPiece>().OnTokenSelected += OnChallengerTokenSelected;
			cToken.GetComponent<TokenPiece>().OnAbilityTriggered += OnChallengerAbilityTriggered;
			GameObject oToken = oppositionCopy.transform.GetChild(index).gameObject;
			Opposition.Tokens.Add(oToken.GetComponent<TokenPiece>());
			oToken.GetComponent<TokenPiece>().OnTokenAttacked += OnOppositionTokenAttacked;
			oToken.GetComponent<TokenPiece>().OnTokenSelected += OnOppositionTokenSelected;
			oToken.GetComponent<TokenPiece>().OnAbilityTriggered += OnOppositionAbilityTriggered;
		}

		// TODO: THIS IS HARD CODED FOR DEVELOPMENT PURPOSES. THIS WILL BE CHANGED ON BUILD 3-------------------------------------------------------------------------
		Challenger.Tokens [0].BoardPosition = 38;
		Vector3 positionedPiece = Pieces [38].transform.position;
		Challenger.Tokens [0].transform.position = new Vector3(positionedPiece.x,1f,positionedPiece.z);

		Challenger.Tokens [1].BoardPosition = 29;
		positionedPiece = Pieces [29].transform.position;
		Challenger.Tokens[1].transform.position= new Vector3(positionedPiece.x,1f,positionedPiece.z);

		Challenger.Tokens [2].BoardPosition = 47;
		positionedPiece = Pieces [47].transform.position; 
		Challenger.Tokens[2].transform.position= new Vector3(positionedPiece.x,1f,positionedPiece.z);

		Challenger.Tokens [3].BoardPosition = 28;
		positionedPiece = Pieces [28].transform.position;
		Challenger.Tokens[3].transform.position= new Vector3(positionedPiece.x,1f,positionedPiece.z);

		Challenger.Tokens [4].BoardPosition = 46;
		positionedPiece = Pieces [46].transform.position;
		Challenger.Tokens[4].transform.position= new Vector3(positionedPiece.x,1f,positionedPiece.z);

		Challenger.Tokens [5].BoardPosition = 37;
		positionedPiece = Pieces [37].transform.position; 
		Challenger.Tokens[5].transform.position= new Vector3(positionedPiece.x,1f,positionedPiece.z);

		Challenger.Tokens [6].BoardPosition = 36;
		positionedPiece = Pieces [36].transform.position;
		Challenger.Tokens[6].transform.position= new Vector3(positionedPiece.x,1f,positionedPiece.z);


		Opposition.Tokens [0].BoardPosition = 42;
		positionedPiece = Pieces [42].transform.position;
		Opposition.Tokens [0].transform.position = new Vector3 (positionedPiece.x, 1f,positionedPiece.z);

		Opposition.Tokens [1].BoardPosition = 26;
		positionedPiece = Pieces [26].transform.position;
		Opposition.Tokens [1].transform.position = new Vector3 (positionedPiece.x, 1f,positionedPiece.z);

		Opposition.Tokens [2].BoardPosition = 62;
		positionedPiece = Pieces [62].transform.position;
		Opposition.Tokens [2].transform.position = new Vector3 (positionedPiece.x, 1f,positionedPiece.z);

		Opposition.Tokens [3].BoardPosition = 34;
		positionedPiece = Pieces [34].transform.position;
		Opposition.Tokens [3].transform.position = new Vector3 (positionedPiece.x, 1f,positionedPiece.z);

		Opposition.Tokens [4].BoardPosition = 52;
		positionedPiece = Pieces [52].transform.position;
		Opposition.Tokens [4].transform.position = new Vector3 (positionedPiece.x, 1f,positionedPiece.z);

		Opposition.Tokens [5].BoardPosition = 43;
		positionedPiece = Pieces [43].transform.position;
		Opposition.Tokens [5].transform.position = new Vector3 (positionedPiece.x, 1f,positionedPiece.z);

		Opposition.Tokens [6].BoardPosition = 44;
		positionedPiece = Pieces [44].transform.position;
		Opposition.Tokens [6].transform.position = new Vector3 (positionedPiece.x, 1f,positionedPiece.z);
		//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
		ResetActions (true);
		ResetActions (false);
		ColliderCover.SetActive (false);
	}
	
	/// <summary>
	/// Sets the Game Over screen.
	/// </summary>
	public void GameOverScreen()
	{
		// make a collider in front of the camera so nobody accidentally presses a button
		// GUI will show game over screen
		// GUI will reset game on keypress
		GameOver = true;
		ColliderCover.SetActive (true);
	}
}
